﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Net;
using System.Xml.Linq;

namespace ReserveMeService
{
    class Base
    {
        //fields
        public string drive = string.Empty;
        public List<Application> appStore = new List<Application>();
        public User user;
        public MasterSet masterSet = new MasterSet();
        public MachineInfo mi = new MachineInfo();
        public ApplicationInfo appInfo = new ApplicationInfo();
        public WebClient feedClient = new WebClient();
        public Stopwatch sp = new Stopwatch();
        public string feedURL = "http://deploytest.wp7dev.co.uk/test/ApplicationList.xml";
   
        public Base()
        {                          
        }

        /// <summary>
        /// Sets up the download event handlers for xml feed, 
        /// this method also gets the local system drive
        /// </summary>
        public void start()
        {
            feedClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(feedClient_DownloadStringCompleted);
            drive = Environment.GetFolderPath(Environment.SpecialFolder.System).Substring(0, 2);
            EventLog.WriteEntry("ReserveMeService", "Starting ReserveMeService");
            getDirectories();
        }       

        /// <summary>
        /// Check to see if there is a folder for local logging
        /// of the service, should this not be the case we
        /// create them
        /// </summary>
        private void getDirectories()
        {                        
            if (Directory.Exists(drive+@"\ReserveMeLogs"))
            {
                startTimer();
            }
            else
            {
                Directory.CreateDirectory(drive+@"\ReserveMeLogs\");
                startTimer();
            }            
        }

        /// <summary>
        /// we set up the timer which will
        /// update our computers information
        /// on the database
        /// </summary>
        public void startTimer()
        {
            //should we have an instance of our stopwatch
            //we stop, reset and start the whole process again
            if (sp != null)
            {
                sp.Stop();
                sp = new Stopwatch();
                sp.Start();
                if (!feedClient.IsBusy)
                {
                    feedClient.DownloadStringAsync(new Uri(feedURL));
                }
                else
                {
                    getApplicationsInstalled();
                }
            }
            //otherwise we create a new instance of the stopwatch
            //and gather the information from our client
            else
            {
                sp = new Stopwatch();
                sp.Start();
                if (!feedClient.IsBusy)
                {
                    feedClient.DownloadStringAsync(new Uri(feedURL));
                }
                else
                {
                    getApplicationsInstalled();
                }
            }
        }

        // prepare to die by stopping everything were doing
        public void close()
        {
            try
            {
               // th.Abort();
                sp.Stop();
                StreamWriter sw = new StreamWriter(drive + @"\ReserveMeLogs\Service.txt");
                sw.WriteLine("Closing Service");
                sw.Close();
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("ReserveMe", ex.Message);
              /*  close();
                StreamWriter sw = new StreamWriter(drive + @"\ReserveMeLogs\ServiceError.txt");
                sw.WriteLine("Could not stop Service");
                sw.WriteLine(string.Format("\t\t\n Error Details \n {0}, \n {1}, \n {2}, \n {3}}", ex.Message, ex.StackTrace, ex.InnerException, ex.Source, ex.Data));
                sw.Close();*/
            }            
        }

        /// <summary>
        /// small function which reads an XML Document
        /// this application populates an IEnumerable
        /// List which is used later for comparison
        /// </summary>
        /// <param name="xml">XML String</param>
        private void loadXML(string xml)
        {
            try
            {
                List<Application> appList = new List<Application>();
                XElement element = XElement.Parse(xml);
                var x = from item in element.DescendantsAndSelf("Applications").Elements("Application")
                        select new Application
                        {
                            Name = (string)item.Attribute("name"),
                            Version = (string)item.Attribute("Version"),
                            Path = (string)item.Attribute("Path"),
                            ExeName = (string)item.Attribute("Executable")
                        };


                appStore = x.ToList();
                MessageMe(x.ToList());
            }
            catch (Exception)
            {
            }
        }

        //This function is called when the webclient (feedClient) has finished
        //downloading the string from a webpage
        void feedClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            loadXML(e.Result);
        }

        /// <summary>
        /// Small function used to test Web connectivity
        /// simply used to print out each application
        /// that we parsed in our XML Document
        /// </summary>
        /// <param name="apps">List of Applications</param>
        void MessageMe(List<Application> apps)
        {
            try
            {
                StreamWriter sw = new StreamWriter(drive+@"\ReserveMeLogs\feedClientResult.txt");
                foreach (Application app in apps)
                {
                    sw.WriteLine(app.Name + "\n");
                }
                //sw.WriteLine(Environment.GetEnvironmentVariable("%systemdrive%").ToString() + "\\ReserveMeLogs\\set.xml");
                sw.Close();
                appStore = apps;
                getApplicationsInstalled();
            }
            catch (Exception e)
            {
                StreamWriter s = new StreamWriter(drive+@"\ReserveMeLogs\MessageMe.txt");
                s.WriteLine("Error in MessageMe");
                s.WriteLine("\n\n" + e.InnerException);
                s.WriteLine(e.StackTrace);
                s.WriteLine(e.Message);
                s.WriteLine(e.Data);
                s.Close();
                getApplicationsInstalled();
            }
        }

        /// <summary>
        /// This function gathers each application installed 
        /// by using our ApplicationInfo Instance appInfo
        /// appInfo makes a call to its built in method
        /// getInstalledApplications()
        /// </summary>
        void getApplicationsInstalled()
        {
            try
            {
                StreamWriter sw = new StreamWriter(drive+@"\ReserveMeLogs\AppsInstalled.txt");
                var info = appInfo.getInstalledApplications();
                sw.WriteLine("\n\n\t\tInstalled Applications\n\n");
                foreach (App app in info.appList)
                {
                    sw.WriteLine(app.Name + "\n");
                }
                sw.Close();
                getHostInfo();
            }
            catch (Exception ex)
            {
                StreamWriter sw = new StreamWriter(drive+@"\ReserveMeLogs\getApplicationsInstalled.txt");
                sw.WriteLine("Error in getApplicationsInstalled()");
                sw.WriteLine(ex.StackTrace);
                sw.WriteLine(ex.InnerException);
                sw.WriteLine(ex.Message);
                sw.WriteLine(ex.Data);
                sw.Close();
                getHostInfo();
            }
        }
        /// <summary>
        /// This Method gathers all the information about the client
        /// Network Connectivity, Users Logged On and Hostname
        /// </summary>
        void getHostInfo()
        {
            try
            {
                mi = new MachineInfo();
                GetHostInformation ghi = new GetHostInformation();
                mi = ghi.getinfo();
                List<string> usernames = new List<string>();
                Process[] list = Process.GetProcesses();
                foreach (Process p in list)
                {
                    string s = GetProcessOwner(p.Id);
                    if (!usernames.Contains(s))
                    {
                        usernames.Add(s);
                    }
                }
                foreach (string name in usernames)
                {
                    if (!name.ToLower().Contains("authority") && !name.ToLower().Contains("no owner") && name.Contains("2"))
                    {
                        try
                        {
                            user = new User();
                            if (name.Contains("2"))
                            {
                                string[] n = name.Split('\\');
                                user.Username = int.Parse(n[1]);
                                break;
                            }
                        }
                        catch (Exception)
                        {
                            user.Username = 21440867;
                        }
                    }
                    else
                    {
                        if(!name.Contains("2"))
                        {
                            user.Username = 100;
                        }
                    }
                }
                Serialize();
            }
            catch (Exception ex)
            {
                StreamWriter sw = new StreamWriter(drive+@"\ReserveMeLogs\MachineError.txt");
                sw.WriteLine("Fuck Me, Something went wrong...");
                sw.WriteLine(ex.Message);
                sw.WriteLine(ex.InnerException);
                sw.WriteLine(ex.StackTrace);
                sw.WriteLine(ex.Data);
                sw.Close();
                Serialize();
                throw;
            }

        }

        /// <summary>
        /// This Method serializes an instance of MasterSet which contains all
        /// other instances of classes such as AdapterInformation, Application,
        /// InstalledAppList, MachineInfo and User
        /// </summary>
        private void Serialize()
        {
            InstalledAppList appList = new InstalledAppList();
            appList = appInfo.getInstalledApplications();
            masterSet = new MasterSet();
            masterSet.user = user.Username;
            masterSet.appInfo = appList;
            masterSet.mInfo = new MachineInfo();
            masterSet.mInfo.HostName = mi.HostName;
            masterSet.mInfo.AdapterInformation = mi.AdapterInformation;
            SerializeXML sxml = new SerializeXML();
            sxml.Serialize(masterSet);
            sp.Stop();
            TimeSpan elapsed = sp.Elapsed;
            StreamWriter sw = new StreamWriter(drive+@"\ReserveMeLogs\Time.txt");
            sw.WriteLine("Time(s): {0}", elapsed.Seconds);
            sw.Close();
            rService.Service1Client svc = new rService.Service1Client();
            svc.update(masterSet);
            reset();
        }

        /// <summary>
        /// resets the applications lifetime objects
        /// </summary>
        private void reset()
        {
            appStore = new List<Application>();
            user = new User();
            masterSet = new MasterSet();
            mi = new MachineInfo();
            appInfo = new ApplicationInfo() ;
        }

        /// <summary>
        /// This Method uses a Management Object Searcher to identify
        /// the Process Owner by ProcessID
        /// </summary>
        /// <param name="processId">the pid of a Process</param>
        /// <returns>String name of the User</returns>
        private string GetProcessOwner(int processId)
        {
            string query = "Select * From Win32_Process Where ProcessID = " + processId;
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
            ManagementObjectCollection processList = searcher.Get();

            foreach (ManagementObject obj in processList)
            {
                string[] argList = new string[] { string.Empty, string.Empty };
                int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
                if (returnVal == 0)
                {
                    // return DOMAIN\user
                    return argList[1] + "\\" + argList[0];
                }
            }
            return "NO OWNER";
        }
       
    }
}
